﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Zen4SyncPOCO.POCO.SQLCEExecution;
using Zen4SyncPOCO.POCO.SQLCEBackup;
using System.Data.SqlServerCe;
using Zen4SyncPOCO.Enum.Admin;
using System.Data;
using System.IO;
using System.IO.Compression;

namespace Zen4SyncClient.ActivityExecutors
{
    /// <summary>
    /// Provides methods to execute activities of the "SQL CE" type.
    /// </summary>
    internal static class SqlCe
    {
        internal static void ExecuteSqlCeExecution( SqlceExecution activity, SqlceExecutionContext context, Guid genericActivityId )
        {
            using ( SqlCeConnection sqlCeConnection = new SqlCeConnection( context.sqlCeExecutionContext_connectionString ) )
            {
                SqlCeCommand sqlCeCommand = new SqlCeCommand( activity.sqlceExecution_sqlQuery, sqlCeConnection );
                foreach ( SqlceExecutionParameter sqlCeExecutionParameter in context.sqlceExecutionContext_parameters )
                {
                    object convertedValue = SqlValueParser.Parse( sqlCeExecutionParameter.sqlceExecutionParameter_value, sqlCeExecutionParameter.sqlceExecutionParameter_sqlValueTypeEnum );
                    SqlCeParameter sqlCeParameter = new SqlCeParameter( sqlCeExecutionParameter.sqlceExecutionParameter_name, convertedValue );
                    sqlCeCommand.Parameters.Add( sqlCeParameter );
                }

                sqlCeConnection.Open();
                sqlCeCommand.ExecuteNonQuery();

                if ( Program.ClientSessionToExecute.clientSession_enableLog )
                {
                    Program.OrchestratorServices.Call( services => services.LogSqlCEExecution( Program.ClientSessionToExecute.clientSession_id, genericActivityId, context ) );  // TODO: WCF Orchestrator
                }
            }
        }

        /// <summary>
        /// Retrieves a value from the database using the SQL query of the activity and parses it into the type given by the activity.
        /// Parses the expected value into the type given by the activity and compare both parsed values.
        /// Exceptions are thrown if values can't be parsed or if parsed values are not equals.
        /// </summary>
        internal static void ExecuteSqlCeCheckData( SqlceCheckData activity, SqlceCheckDataContext context )
        {
            using ( SqlCeConnection sqlCeConnection = new SqlCeConnection( context.sqlceCheckDataContext_connectionString ) )
            {
                SqlCeCommand sqlCeCommand = new SqlCeCommand( activity.sqlceCheckData_sqlQuery, sqlCeConnection );
                foreach ( SqlceCheckDataParameter sqlCeCheckDataParameter in context.sqlceCheckDataContext_parameters )
                {
                    object convertedValue = SqlValueParser.Parse( sqlCeCheckDataParameter.sqlceCheckDataParameter_value, sqlCeCheckDataParameter.sqlceCheckDataParameter_sqlValueTypeEnum );
                    SqlCeParameter sqlCeParameter = new SqlCeParameter( sqlCeCheckDataParameter.sqlceCheckDataParameter_name, convertedValue );
                    sqlCeCommand.Parameters.Add( sqlCeParameter );
                }

                object actualValue = null;
                byte[] expectedValue = activity.sqlceCheckData_expectedValue;
                object parsedActualValue = null;
                object parsedExpectedValue = null;
                SqlValueTypeEnum valuesType = activity.sqlceCheckData_sqlValueTypeEnum;

                sqlCeConnection.Open();

                actualValue = sqlCeCommand.ExecuteScalar();

                sqlCeConnection.Close();

                try
                {
                    parsedExpectedValue = SqlValueParser.Parse( expectedValue, valuesType );
                }
                catch ( Exception exc )
                {
                    string exceptionMessage = UnableToParseExceptionMessage( valuesType, Encoding.UTF8.GetString( expectedValue ), exc.Message );
                    throw new Exception( exceptionMessage );
                }

                try
                {
                    parsedActualValue = SqlValueParser.Parse( actualValue, valuesType );
                }
                catch ( Exception exc )
                {
                    string exceptionMessage = UnableToParseExceptionMessage( valuesType, actualValue.ToString(), exc.Message );
                    throw new Exception( exceptionMessage );
                }

                if ( !SqlValueComparer.ValuesAreEquals( valuesType, parsedExpectedValue, parsedActualValue ) )
                {
                    throw new Exception( CheckDataExceptionMessage( valuesType, parsedExpectedValue.ToString(), parsedActualValue.ToString() ) );
                }
            }
        }

        internal static void ExecuteSqlCeBackup( SqlceBackup activity, SqlceBackupContext context, Guid genericActivityId )
        {
            string sdfFilePath = Path.Combine( context.sqlceBackupContext_sdfFileDirectory, context.sqlceBackupContext_sdfFileName );

            if ( Program.DebugLogEnabled ) Program.Logger.LogMessage( String.Format( "Starting SQL CE Backup of {0}", sdfFilePath ) );

            using ( FileStream fileSdf = File.OpenRead( sdfFilePath ) )
            {
                if ( Program.DebugLogEnabled ) Program.Logger.LogMessage( "SQL CE Backup : File open" );

                using ( MemoryStream memoryStream = new MemoryStream( 100 ) )
                {
                    using ( GZipStream compress = new GZipStream( memoryStream, CompressionMode.Compress ) )
                    {
                        fileSdf.CopyTo( compress );

                        if ( Program.DebugLogEnabled ) Program.Logger.LogMessage( "SQL CE Backup : File compressed" );

                        Program.OrchestratorServices.Call( services => services.LogBackupActivity( Program.ClientSessionToExecute.clientSession_id, genericActivityId, memoryStream.ToArray() ) );

                        if ( Program.DebugLogEnabled ) Program.Logger.LogMessage( "SQL CE Backup : File sent" );
                    }
                }
            }
        }


        /// <summary>
        /// Exception message indicating the impossibility to parse an object into a certain type.
        /// </summary>
        private static string UnableToParseExceptionMessage( SqlValueTypeEnum type, string objectToString, string parseExceptionMessage )
        {
            return String.Format( "SqlServerCheckData : Unable to parse object into {0}.\nobject ToString() : {1}\nParse Exception Message : {2}", Enum.GetName( typeof( SqlValueTypeEnum ), type ), objectToString, parseExceptionMessage );
        }

        /// <summary>
        /// Exception message indicating the difference between two values supposed to be equals.
        /// </summary>
        private static string CheckDataExceptionMessage( SqlValueTypeEnum expectedType, string expectedValue, string actualValue )
        {
            return String.Format( "SqlCeCheckData : Expected {0} {1} but was {2}", Enum.GetName( typeof( SqlValueTypeEnum ), expectedType ), expectedValue, actualValue );
        }
    }
}
